package main

import (
	"encoding/json"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/umputun/go-flags"
	"golang.org/x/text/encoding/charmap"
	"log"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strconv"
	"strings"
	"time"
)

type Config struct {
	PHPSESSID string `long:"PHPSESSID" env:"PHPSESSID"`
	Password  string `long:"Password" env:"Password"`
	IdsRaw    string `long:"ids" env:"IDS"`
}

func main() {
	var config = &Config{}
	if _, err := flags.Parse(config); err != nil {
		log.Fatal(err)
	}
	cfgJson, err := json.Marshal(*config)
	if err != nil {
		log.Fatal(err)
	}
	log.Printf("Config is %s", string(cfgJson))

	jar, err := cookiejar.New(nil)
	if err != nil {
		panic(err)
	}
	u, err := url.Parse("https://l2on.net")
	if err != nil {
		panic(err)
	}
	jar.SetCookies(u, []*http.Cookie{
		{
			Name:   "PHPSESSID",
			Value:  config.PHPSESSID,
			Domain: ".l2on.net",
			Path:   "/",
		},
		{
			Name:  "password",
			Value: config.Password,
		},
	})
	client := http.Client{Timeout: 60 * time.Second, Jar: jar}

	for {
		fetch(config, client)
		time.Sleep(1 * time.Minute)
	}
}

func fetch(config *Config, client http.Client) {
	buyItems := make(map[string][]map[string]string)
	sellItems := make(map[string][]map[string]string)

	for _, id := range strings.Split(config.IdsRaw, ",") {
		request, err := http.NewRequest("GET", fmt.Sprintf("https://l2on.net/?c=market&a=item&id=%d", id), nil)
		if err != nil {
			log.Printf("[ERROR] while build request %s", err.Error())
			continue
		}

		resp, err := client.Do(request)
		if err != nil {
			log.Printf("[ERROR] while do request %s", err.Error())
			continue
		}

		if resp.StatusCode < 200 || resp.StatusCode > 299 {
			log.Printf("[ERROR] wrong response status code %d", resp.StatusCode)
			resp.Body.Close()
			continue
		}

		decoder := charmap.Windows1251.NewDecoder()
		reader := decoder.Reader(resp.Body)

		doc, err := goquery.NewDocumentFromReader(reader)
		if err != nil {
			log.Printf("[ERROR] while goquery new document from reader %s", err.Error())
			continue
		}
		resp.Body.Close()

		itemName := doc.Find("h1").Text()

		buyItems[itemName] = parseTable(doc.Find("#group_buy").First())
		sellItems[itemName] = parseTable(doc.Find("#group_sell").First())
	}

	sellItemMinPrice := make(map[string]int64)
	for s, i := range sellItems {
		for _, m := range i {
			if m["fresh"] != "true" {
				continue
			}
			price, err := strconv.ParseInt(strings.Replace(m["Цена"], " ", "", -1), 10, 64)
			if err != nil {
				log.Printf("[ERROR] while parse sell price int %s", err.Error())
				continue
			}
			minPrice, e := sellItemMinPrice[s]
			if !e {
				sellItemMinPrice[s] = price
			}
			if minPrice > price {
				sellItemMinPrice[s] = price
			}
		}
	}
	buyItemMaxPrice := make(map[string]int64)
	for s, i := range buyItems {
		for _, m := range i {
			if m["fresh"] != "true" {
				continue
			}
			price, err := strconv.ParseInt(strings.Replace(m["Цена"], " ", "", -1), 10, 64)
			if err != nil {
				log.Printf("[ERROR] while parse buy price int %s", err.Error())
				continue
			}
			maxPrice, e := buyItemMaxPrice[s]
			if !e {
				buyItemMaxPrice[s] = price
			}
			if maxPrice < price {
				buyItemMaxPrice[s] = price
			}
		}
	}

	for name, sellPrice := range sellItemMinPrice {
		_, e := buyItemMaxPrice[name]
		if !e {
			continue
		}
		buyMaxPrice := buyItemMaxPrice[name]
		if buyMaxPrice > sellPrice {
			log.Printf("!!! ALARM Цена покупки выше цены продажи diff = %d. Итем %s\n", buyMaxPrice-sellPrice, name)
		}
	}
}

func parseTable(tableGroup *goquery.Selection) []map[string]string {
	headermap := make(map[int]string)
	valsmap := make([]map[string]string, 0)
	tableGroup.Find("tr").Each(func(indextr int, rowhtml *goquery.Selection) {
		rowhtml.Find("th").Each(func(indexth int, tableheading *goquery.Selection) {
			headermap[indexth] = tableheading.Text()
		})
		valmap := make(map[string]string)
		valmap["fresh"] = fmt.Sprintf("%t", rowhtml.HasClass("fresh"))
		hasContent := false
		rowhtml.Find("td").Each(func(indexth int, tablecell *goquery.Selection) {
			valmap[headermap[indexth]] = tablecell.Text()
			hasContent = true
		})
		if hasContent {
			valsmap = append(valsmap, valmap)
		}
	})
	return valsmap
}
